#!/usr/bin/python
# -*- coding: utf-8 -*-

class OBJ(object):
    """Stellt Methoden zum einlesen (parsen) von Punkt und obj Dateien bereit"""
    def __init__(self, filename):
        self.__filename = filename
        
        self.__MAX_FACE_VERTICES = 4 # Cube Flachen-Polygone hat 4 Ecken
        self.__MIN_DIMENSION = 2 # 2D Punkte (x,y)
        self.__V = 'v'
        self.__VT = 'vt'
        self.__VN = 'vn'
        self.__F = 'f'
        self.__prefixes = [self.V, self.VT, self.VN, self.F]
        
        self.__vertices = []
        self.__textures = []
        self.__vertex_normals = []
        self.__faces = []        
        
        self.__SIZE_OF_VERTEX_COORDINATES = self.compute_size_of_vertex_coordinates()

        # initial obj einlesen
        self.read_obj_file()
        
        # strukur bestimmen
        self.__faces_structure = self._get_faces_structure()

    def get_faces_structure(self):
        return self.__faces_structure


    def set_faces_structure(self, value):
        self.__faces_structure = value


    def get_min_dimension(self):
        return self.__MIN_DIMENSION


    def get_max_face_vertices(self):
        return self.__MAX_FACE_VERTICES

        
    def get_v(self):
        return self.__V


    def get_vt(self):
        return self.__VT


    def get_vn(self):
        return self.__VN


    def get_f(self):
        return self.__F


    def get_prefixes(self):        
        return self.__prefixes


    def compute_size_of_vertex_coordinates(self):
        """Berrechnet anzahl der Vertexkoordinaten"""
        return 5
        
    def get_size_of_vertex_coordinates(self):
        """2,3,4 Die Anzahl der Koordinaten die ein Vertex besitzt"""
        return self.__SIZE_OF_VERTEX_COORDINATES


    def set_size_of_vertex_coordinates(self, value):
        self.__SIZE_OF_VERTEX_COORDINATES = value


    def del_filename(self):
        del self.__filename


    def get_filename(self):
        return self.__filename


    def get_vertices(self):
        return self.__vertices


    def get_textures(self):
        return self.__textures


    def get_vertex_normals(self):
        return self.__vertex_normals


    def get_faces(self):
        return self.__faces
        
    
    def _get_faces_structure(self):
        """Ermittelt die Struktur v/vt/vn, v/vn, v/vt, v//vn des Indexed Faceset"""
        lis = [self.V]
        delimiter = '//'
        if self.textures:
            delimiter = '/'
            lis.append(self.VT)
        if self.vertex_normals:
            lis.append(self.VN)
        return delimiter.join(lis)        
    


    
    
    def _get_faces(self, faces_line):
        """Dereferenziert die Indizes im uebergebenen Face (faces_line) und gibt zugehoerige Punktkoordinaten als 1 Dimensionale Liste zurueck"""
        polygon = []
        for point in faces_line:
#            vertice = []
            point = point.split("/")            
            if len(point)==1: # v
                polygon.extend(self.vertices[int(point[0])-1])
            elif len(point)==3: # v/vt/vn
                if point[1]=="": # ['v','','vt']
                    polygon.extend(self.vertices[int(point[0])-1])
                    #vertice.extend([]) keine leeren listen!
                    polygon.extend(self.vertex_normals[int(point[2])-1])
                else:
                    polygon.extend(self.vertices[int(point[0])-1])
                    polygon.extend(self.textures[int(point[1])-1])
                    polygon.extend(self.vertex_normals[int(point[2])-1])
            else:
                raise Exception("Fehlerhafte .obj Datei!")
#            polygon.extend(vertice)
        return polygon


    def _is_relevant(self, line):
        """Gibt True zurueck wenn die Zeile Informationen enthaelt die geparsed werden sollen. Momentan nur 3D."""
        prefixes = self.prefixes
        values = line.split()
        length = len(values)
        pf = None if length==0 else values[0]

        if pf not in prefixes:        
            return False         
        if length > self.MAX_FACE_VERTICES+1 or length < self.MIN_DIMENSION+1:
            return False
        if length == 3 and pf != self.VT:
            raise AttributeError("Ungueltiger Faceset Eintrag, bekomme die Zeile: %s, prefix: %s, length: %i" % (line, pf, length))
        return True
    
    
    def read_obj_file(self, filename=None):
        """Liest ein Indexed Faceset .obj ein und fuellt die eigenen vertex-, normals-, textures-, faces Listen""" 
        if filename==None and self.filename==None:
            raise AttributeError('Keine .obj Datei angegeben!')
        elif filename==None:            
            filename = self.filename
        # default ist zeilenweise einlesen
#        try
        for line in [l.split() for l in file(filename) if self._is_relevant(l)==True]:
            prefix = line[0]
            length = len(line)
            if prefix == self.V:
                self.vertices.append([float(line[1]), float(line[2]), float(line[3])])
            elif prefix == self.VT:
                if length==4:
                    self.textures.append([float(line[1]), float(line[2]), float(line[3])])
                else: # length==3, assert: Nur 3 oder 4 Texturkoordinaten
                    self.textures.append([float(line[1]), float(line[2])])
            elif prefix == self.VN:
                self.vertex_normals.append([float(line[1]), float(line[2]), float(line[3])])
            elif prefix == self.F:
                self.faces.append( self._get_faces(line[1:]))            
            else:
                raise Exception("Fehlerhafte Datei! Bekam die Zeile: %s." % line)


    def __repr__(self):
        output = []
        if len(self.faces)==0:
            output = "Datei wurde noch nicht gelesen ist fehlerhaft!"
        else:
            output.append("Struktur der Flachenpolygone: %s" % self.faces_structure)
            output.append("Anzahl der Vertices(Eckpunkte): %s" % len(self.vertices))
            output.append("Anzahl der Texturen: %s" % len(self.textures))
            output.append("Anzahl der Vertex-Normalen: %s" % len(self.vertex_normals))
            output.append("Anzahl der Faces(Polygone): %s" % len(self.faces))
        return '\n'.join(output)


    # FIXME: noch korrekt implementieren
    def get_vertex_list(self):
        """Gibt liste der Punkte zurueck wie sie zum initialisieren eines VBO benoetigt werden"""
        vertex_list = []
        
        # TODO: assert parser gibt nur gueltige obj datei zurueck
        vertices = self.vertices
        vertex_normals = self.vertex_normals
        textures = self.textures
        
        # [ [vetexliste], [normalenliste], [texturliste]   
        lis = [lis for lis in (vertices, vertex_normals, textures) if lis != []]
        structure = []
        # TODO: andere facestets testen, evtl diese methode in den parser setzen 
        if vertex_normals and textures:
            vertex_list = zip(lis[0], lis[1], lis[2])
        elif vertex_normals:
            vertex_list = zip(lis[0], lis[1])
        else:
            vertex_list = lis[0]
            print vertex_list[0]
            return vertex_list




    textures = property(get_textures, None, None, None)
    vertex_normals = property(get_vertex_normals, None, None, None)
    faces = property(get_faces, None, None, None)
    vertices = property(get_vertices, None, None, None)
    SIZE_OF_VERTEX_COORDINATES = property(get_size_of_vertex_coordinates, set_size_of_vertex_coordinates, None, "SIZE_OF_VERTEX_COORDINATES's docstring")
    filename = property(get_filename, None, del_filename, None)
    V = property(get_v, None, None, "V's docstring")
    VT = property(get_vt, None, None, "VT's docstring")
    VN = property(get_vn, None, None, "VN's docstring")
    F = property(get_f, None, None, "F's docstring")
    prefixes = property(get_prefixes, None, None, "prefixes's docstring")
    MAX_FACE_VERTICES = property(get_max_face_vertices, None, None, "MAX_FACE_VERTICES's docstring")
    MIN_DIMENSION = property(get_min_dimension, None, None, "MIN_DIMENSION's docstring")
    faces_structure = property(get_faces_structure, set_faces_structure, None, "faces_structure's docstring")
   

if __name__ == "__main__":
#    dat = "../data/elephant.obj"
    dat = "../data/cube_standard.obj"
    obj = OBJ(dat)
    print obj
    print
    print 'faces: ', obj.faces[:1]
    print 'normals: ', obj.vertex_normals[:1]
    print 'textures: ',  obj.textures[:1]
    print 'vertices: ',  obj.vertices[:1]
    ## dat = ""
    ## reader = OBJ(dat)

    

    

    

                                
